package eu.webhib.extgui.controller.tree.data;

import eu.webhib.extgui.controller.intf.TreeDataProvider;
import eu.webhib.extgui.data.intf.GuiParentChildEntity;
import eu.webhib.extgui.data.intf.DataItem;
import eu.webhib.extgui.data.service.intf.GuiParentChildService;
import java.util.List;
import org.apache.log4j.Logger;

/**
 * This TreeDataProvider holds one top level transient string label beneath
 * persistent entities according to their parent/child relationship are located.
 * @param T type of the parent/Child entities hold under the labeled container item
 */
public class ParentTreeDataProvider<T extends GuiParentChildEntity<T>> implements TreeDataProvider {

   private static final Logger log = Logger.getLogger(ParentTreeDataProvider.class);
   /** string label of the transient top level item */
   private String containerLabel;
   /** service for the persistent entities */
   private GuiParentChildService<T> entityService;
   /** transient top level container item holding all persistent entity items */
   private TreeLabelItem<T> containerItem;

   /* Construct a TreeDataProvider with the given containerLabel which retrieves the entities from the
    * given entityService
    */
   public ParentTreeDataProvider(String containerLabel, GuiParentChildService<T> entityService) {
      this.containerLabel = containerLabel;
      this.entityService = entityService;
   }

   /**
    * Retrieve the root data item to display in a tree component
    */
   @Override
   public DataItem getRoot() {
      log.info("getRoot");
      containerItem = new TreeLabelItem<T>(containerLabel);
      /** navigation is possible from container parent to its children (the top level entities),
       * but not from the top level entities to the view container */
      containerItem.setChildren(entityService.getRootList());
      return containerItem;
   }

   /**
    * Returns the parent of this item in the view
    */
   @Override
   public DataItem getViewParent(DataItem dataItem) {
      log.trace("getViewParent: " + dataItem + " " + dataItem.getClass());

      if (dataItem == containerItem) {
         return null;
      }
      DataItem parent = ((GuiParentChildEntity) dataItem).getParent();
      if (parent == null) {
         /** if it is a top level entity without an parent it is located under the label container */
         return containerItem;
      }
      return parent;
   }

   /**
    * Returns the children list of this item in the view
    */
   @Override
   public List<DataItem> getViewChildren(DataItem dataItem) {
      if (dataItem == containerItem) {
         // container item is of type TreeLabelItem
         @SuppressWarnings("unchecked")
         List<DataItem> ret = ((TreeLabelItem) dataItem).getChildren();
         return ret;
      }
      // all other items are of type GuiParentChildEntity
      @SuppressWarnings("unchecked")
      List<DataItem> ret = (List<DataItem>) (((GuiParentChildEntity) dataItem).getChildren());
      return ret;
   }

   @Override
   @SuppressWarnings("unchecked")
   public boolean moveTo(DataItem nodeToMove, DataItem newViewParent) {
      /** if the source node represents an entity */
      assert (nodeToMove != null);
      assert (newViewParent != null);
      // TODO: instanceof T
      if (nodeToMove instanceof GuiParentChildEntity) {
         /** unchecked cast */
         T moveToNodeEntity = (T) nodeToMove;
         T newEntityParent;
         /** check for container root */
         if (newViewParent == containerItem) {
            newEntityParent = null;
         } else {
            if (newViewParent instanceof GuiParentChildEntity) {
               /** unchecked cast */
               newEntityParent = (T) newViewParent;
            } else {
               return false;
            }
         }
         /** check for not moving to its own child */
         if (!isChildOf(newEntityParent, moveToNodeEntity)) {
            /** follow move in data store */
            entityService.moveToNewParent(moveToNodeEntity, newEntityParent);
            return true;
         }
      }
      /** this move is not allowed */
      return false;
   }

   /**
    * Whether the entity is a direct or indirect child of the given parent
    */
   private boolean isChildOf(T entity, T parent) {
      while (entity != null) {
         if (entity.equals(parent)) {
            log.info("isChildOf: " + entity + " parent: " + parent);
            return true;
         }
         entity = entity.getParent();
      }
      return false;
   }
}
